package security.diffie.hellmann;

import java.math.BigInteger;
import java.util.Random;

/**
 * Extension de la classe BigInteger.
 * @author Jean-François Augé
 */
public class BigInteger_ extends BigInteger {
	
	private static final long serialVersionUID = 1L;
	
	public static final BigInteger TWO = new BigInteger("2");
	public static final BigInteger THREE = new BigInteger("3");
	public static final BigInteger FOUR = new BigInteger("4");
	public static final BigInteger FIVE = new BigInteger("5");
	public static final BigInteger SIX = new BigInteger("6");
	public static final BigInteger SEVEN = new BigInteger("7");
	public static final BigInteger EIGHT = new BigInteger("8");
	
	/**
	 * Constructeur.
	 * @param val
	 */
	public BigInteger_(String val) {
		super(val);
	}
	
	/**
	 * Constructeur.
	 * @param val
	 * @param radix, la base numérique
	 */
	public BigInteger_(String val, int radix) {
		super(val, radix);
	}
	
	/**
	 * Initialise le générateur aléatoire.
	 * @return {@link Random}
	 */
	// TODO faire quelque chose de plus compliqué?
	public static Random initRNG() {
		Random rng = new Random();
		rng.setSeed(System.nanoTime());
		return rng;
	}
	
	/**
	 * Génére un nombré aléatoire impaire.
	 * @param rng
	 * @param length
	 * @return {@link BigInteger}
	 */
	public static BigInteger generate_odd(Random rng, int length) {
		byte[] tab = new byte[length/8];
		rng.nextBytes(tab);
		BigInteger b = new BigInteger(tab);
		b = b.flipBit(length-1);
		b = b.flipBit(0);
		return b.abs();
	}
	
	/**
	 * Génère un nombre aléatoire.
	 * @param rng
	 * @param length
	 * @return {@link BigInteger}
	 */
	public static BigInteger generate(Random rng, int length) {
		byte[] tab = new byte[length/8];
		rng.nextBytes(tab);
		BigInteger b = new BigInteger(tab);
		b = b.flipBit(length-1);
		return b.abs();
	}
	
	/**
	 * Exponentiation rapide.
	 * @param a
	 * @param b
	 * @return {@link BigInteger}
	 */
	public static BigInteger pow(BigInteger a, BigInteger b) {
		BigInteger x, y, z;
		x = a;
		y = b;
		z = ONE;
		
		while (!y.equals(ZERO)) {
			if(y.mod(TWO).equals(ONE)) {
				z = z.multiply(x);
				y = y.subtract(ONE);
			}
			else {
				x = x.multiply(x);
				y = y.divide(TWO);
			}
		}
		
		return z;
	}
	
	/**
	 * Test de primalité de Fermat.
	 * @param b
	 * @return boolean
	 */
	public static boolean fermat(BigInteger b) {
		BigInteger b2 = b.subtract(ONE);
		
		if(TWO.modPow(b2, b).equals(ONE))
			if(THREE.modPow(b2, b).equals(ONE))
				if(FIVE.modPow(b2, b).equals(ONE))
					if(SEVEN.modPow(b2, b).equals(ONE))
						return true;
		
		return false;
	}
	
}
